package lt.mockos.app.client.cpu.presenter;

import java.util.LinkedList;

import lt.mockos.app.client.cpu.event.StepEvent;
import lt.mockos.app.client.cpu.event.SuperChangeEvent;
import lt.mockos.app.client.cpu.hw.Converter;
import lt.mockos.app.client.cpu.hw.Interpreter;
import lt.mockos.app.client.cpu.hw.Memory;
import lt.mockos.app.client.cpu.hw.RealMachine;
import lt.mockos.app.client.cpu.hw.descriptor.Descriptor;
import lt.mockos.app.client.cpu.hw.descriptor.Descriptor.DescriptorType;
import lt.mockos.app.client.cpu.hw.descriptor.ProcessDescriptor;
import lt.mockos.app.client.cpu.hw.descriptor.ResourceDescriptor;
import lt.mockos.app.client.cpu.hw.type.ProcessStatus;
import lt.mockos.app.client.cpu.hw.type.ProcessType;
import lt.mockos.app.client.cpu.hw.type.Registers;
import lt.mockos.app.client.widget.MemTable;
import lt.mockos.app.client.widget.TextField;
import lt.mockos.app.client.widget.VMControls;
import lt.mockos.app.client.widget.VMControls.VM;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;

public class CPUPresenter {

    public interface CPUDisplay extends IsWidget {

        TextArea getOutput();

        TextBox getInput();

        TextArea getTask();

        CheckBox getStepMode();

        Button getStepBtn();

        Button getRunBtn();

        Button getEnterBtn();

        MemTable getSuperMem();

        void setCH3(TextField cH3);

        MemTable getUserMem();

        TextField getCH3();

        TextField getPI();

        TextField getCH1();

        TextField getTI();

        TextField getCH2();

        TextField getSI();

        TextField getPTR();

        TextField getTIME();

        TextField getSP();

        TextField getAR();

        TextField getBR();

        TextField getSF();

        TextField getMODE();

        TextField getDS();

        TextField getSS();

        TextField getCP();

        TextField getCS();

        Button getLoadBtn();

        Button getUnloadBtn();

        VMControls getVmControls();

        Button getStopBtn();

    }

    private CPUDisplay view;

    private SimpleEventBus eventBus = new SimpleEventBus();
    private RealMachine rm;

    private int row = 0;
    private int cel = 0;

    public CPUPresenter(CPUDisplay view) {
        this.view = view;
        initRealMachine();
        SuperChangeEvent.register(eventBus, new SuperChangeEvent.Handler() {

            @Override
            public void onChange(SuperChangeEvent event) {
                updateSuperMemory();

            }
        });

        StepEvent.register(eventBus, new StepEvent.Handler() {

            @Override
            public void onStep(StepEvent event) {

                //rm.getRegisters().TIME = 2;
                ProcessDescriptor current = null;
                if (rm.getRegisters().TI == 1){
                    current = rm.processesQueue.remove();
                    rm.getRegisters().TI = 0;
                    rm.processesQueue.add(current);
                }

                updateRegisters();
                updateMemory();
                Interpreter.CMD cmd = null;
                try {
                    cmd = Interpreter.CMD.valueOf(event.getMessage());
                } catch (Exception e) {

                }
                if (cmd != null) {
                    switch (cmd) {
                        case PRNS:
                        case PRNW:
                            String text = getView().getOutput().getText();
                            String t = Converter.intToString(rm.getRegisters().AR);
                            text += t.contentEquals("crlf") ? "\n" : t;
                            getView().getOutput().setText(text);
                            break;
                        case SCNW:
                            getView().getInput().setEnabled(true);
                            getView().getInput().setFocus(true);
                            getView().getEnterBtn().setEnabled(true);
                            current.setStatus(ProcessStatus.BLOCK);
                            ResourceDescriptor rd = (ResourceDescriptor)rm.getSuperMemory().getDescriptorByTitle("Input");
                            rm.getResourcesManager().askResource(rd, current);
                            
//                            runner.cancel();
                            break;
                        case HALT:
                            ProcessDescriptor pd = (ProcessDescriptor) rm.getSuperMemory().getDescriptorByTitle(getView().getVmControls().getSelectedVM().name());
                            rm.getMemory().deallocateVMMemory(current.getSavedRegisters().PTR);
                            rm.processesQueue.remove(current);
                            rm.getProcessesManager().removeProcess(current);
                            updateMemory();
                            updateRegisters();
                            if (rm.processesQueue.size() == 0) {
                            getView().getLoadBtn().setEnabled(true);
                            getView().getUnloadBtn().setEnabled(false);
                            getView().getRunBtn().setEnabled(false);
                            //getView().getStepBtn().setEnabled(false);
                            getView().getStopBtn().setEnabled(false);
                            //getView().getStepMode().setValue(false);
                            //getView().getStepMode().setEnabled(false);
                            }
                            getView().getUserMem().getCell(row, cel).getElement().getStyle().setBackgroundColor("#FFFFFF");
                            if (rm.getRegisters().PI > 0) {
                                Window.alert("Invalid command");
                            }
//                            runner.cancel();
                            break;
                        default:
                            break;
                    }
                }
            }
        });
        getView().getLoadBtn().addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                // rm.getMemory().allocateVMMemory(rm.getRegisters());
                // rm.getMemory().loadTask(rm.getRegisters().PTR, getView().getTask().getText());

                ProcessDescriptor pd = rm.loader(getView().getTask().getText(), getView().getVmControls().getSelectedVM().name());
                rm.processesQueue.add(pd);

                // ProcessDescriptor pd =
                // (ProcessDescriptor)rm.getSuperMemory().getDescriptorByTitle(getView().getVmControls().getSelectedVM().name());
                // rm.setRegisters(pd.getSavedRegisters());

                // ProcessesManager.CreateProcess(VM.VM1.name(), null, ProcessPriority.USER, ProcessType.VM);
                updateMemory();
                updateRegisters();
                getView().getLoadBtn().setEnabled(false);
                getView().getUnloadBtn().setEnabled(true);
                getView().getRunBtn().setEnabled(true);
                //getView().getStepMode().setEnabled(true);
            }
        });

        getView().getUnloadBtn().addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                getView().getLoadBtn().setEnabled(true);
                getView().getUnloadBtn().setEnabled(false);
                ProcessDescriptor pd = (ProcessDescriptor) rm.getSuperMemory().getDescriptorByTitle(getView().getVmControls().getSelectedVM().name());
                rm.getMemory().deallocateVMMemory(pd.getSavedRegisters().PTR);
                rm.getProcessesManager().removeProcess(pd);

                updateMemory();
                updateRegisters();
                getView().getTask().setReadOnly(false);
                getView().getInput().setText("");
                getView().getOutput().setText("");
                getView().getRunBtn().setEnabled(false);
                //getView().getStepMode().setEnabled(false);
                // getView().getStepBtn().setEnabled(false);

            }
        });

        getView().getRunBtn().addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                // rm.getRegisters().TIME = 3;
                // rm.callRun();
                // getView().getStepMode().setEnabled(false);
                // getView().getRunBtn().setEnabled(false);
                String title = getView().getVmControls().getSelectedVM().name();
                ProcessDescriptor pd = (ProcessDescriptor)rm.getSuperMemory().getDescriptorByTitle(title);
                pd.setStatus(ProcessStatus.READY);
                
                getView().getRunBtn().setEnabled(false);
                getView().getStopBtn().setEnabled(true);
            }
        });

        getView().getStopBtn().addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                String title = getView().getVmControls().getSelectedVM().name();
                ProcessDescriptor pd = (ProcessDescriptor)rm.getSuperMemory().getDescriptorByTitle(title);
                pd.setStatus(ProcessStatus.STOP);

                getView().getRunBtn().setEnabled(true);
                getView().getStopBtn().setEnabled(false);

//                  runner.cancel();
//                eventBus.fireEvent(new StepEvent(CMD.HALT.name()));
            }
        });

        getView().getEnterBtn().addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                String text = getView().getInput().getText();
                //rm.getRegisters().AR = Converter.stringToInt(text);
                
                ResourceDescriptor rd = (ResourceDescriptor)rm.getSuperMemory().getDescriptorByTitle("Input");
                rd.getUsingProcess().setStatus(ProcessStatus.READY);
                rd.getUsingProcess().getSavedRegisters().AR = Converter.stringToInt(text);
                rm.getResourcesManager().freeResource(rd);
                
                //rm.callRun();
                getView().getInput().setEnabled(false);
                getView().getEnterBtn().setEnabled(false);
//                runner.schedule(1000);

            }
        });

        getView().getStepMode().addValueChangeHandler(new ValueChangeHandler<Boolean>() {

            @Override
            public void onValueChange(ValueChangeEvent<Boolean> event) {
                if (event.getValue())
                    rm.getExecutor().cancel();
                else
                    rm.getExecutor().scheduleRepeating(RealMachine.EXECUTOR_WAIT_TIME);
                //getView().getRunBtn().setEnabled(!event.getValue());
                getView().getStepBtn().setEnabled(event.getValue());

            }
        });

        getView().getStepBtn().addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                rm.doStep();
            }
        });
        
   
        getView().getVmControls().addValueChangeHandler(new ValueChangeHandler<String>() {

            @Override
            public void onValueChange(ValueChangeEvent<String> event) {
                ProcessDescriptor pd = (ProcessDescriptor) rm.getSuperMemory().getDescriptorByTitle(event.getValue());
                if ((null == pd) || (pd != null && pd.getSavedRegisters().SI == 1)) {
                    rm.setRegisters(new Registers());
                    getView().getLoadBtn().setEnabled(true);
                    getView().getUnloadBtn().setEnabled(false);
                    getView().getRunBtn().setEnabled(false);
                    //getView().getStepMode().setEnabled(false);

                } else {
                    rm.setRegisters(pd.getSavedRegisters());
                    getView().getLoadBtn().setEnabled(false);
                    getView().getUnloadBtn().setEnabled(true);
                    if (pd.getStatus() == ProcessStatus.STOP){
                        getView().getRunBtn().setEnabled(true);
                        getView().getStopBtn().setEnabled(false);
                    }
                    else{
                        getView().getRunBtn().setEnabled(false);
                        getView().getStopBtn().setEnabled(true);
                 
                    }
                    //getView().getStepMode().setEnabled(true);

                }
                updateMemory();
                updateRegisters();

                VM vm = VM.valueOf(event.getValue());
                ProcessStatus ps = getVMProcessStatus(vm);
                boolean isloaded = ps != null;
                if (isloaded) {
                    switch (ps) {
                        case BLOCK:
                            break;
                        case READY:
                            break;
                        case RUN:

                            break;
                        case STOP:
                            break;
                        default:
                            break;
                    }
                }
            }
        });
        rm.init();
    }
    
    private void initRealMachine(){
        rm = new RealMachine(eventBus){
            @Override
            public void doStep() {
//              LinkedList<ProcessDescriptor> descriptors = rm.getSuperMemory().getProcessDescriptors();                
//              if (rm.processesQueue.size() == 0)
//              {
//                  for (ProcessDescriptor d : descriptors){
//                      if (d.getType() == ProcessType.VM)
//                          rm.processesQueue.add(d);
//                  }
//              }
                                
                ProcessDescriptor current = processesQueue.peek();
                if (current == null)
                    return;
                
                int j = 1;
                while (current.getStatus() != ProcessStatus.READY){
                    if (current.getStatus() == ProcessStatus.BLOCK){
                        ResourceDescriptor rd = current.getAskedResource();
                        if (rd.getUsingProcess() == null && current.getAskedResource() == rd){
                            rd.setUsingProcess(current);
                            if (rd.getTitle().contentEquals("Input")){
                                getView().getEnterBtn().setEnabled(true);
                                getView().getInput().setEnabled(true);
                            }
                            //current.setStatus(ProcessStatus.READY);
                            //getView().getUserMem().getCell(r, c).getElement().getStyle().setBackgroundColor("#FFFF00");
                            return;
                        }
                    }
                    
                    current = processesQueue.remove();
                    if (null == current)
                        return;
                                      
                    processesQueue.add(current);
                    current = processesQueue.peek();
                    if (j++ >= processesQueue.size())
                        return;
                }

                setRegisters(current.getSavedRegisters());

                getView().getUserMem().getCell(row, cel).getElement().getStyle().setBackgroundColor("#FFFFFF");
                int x = getRegisters().CP / 10;
                int y = getRegisters().CP % 10;
                int ptr = getRegisters().PTR + Memory.BLOCKS_PER_DS + x;
                int i = Converter.stringToInt(getMemory().getMemory()[ptr]) + y;
                int c = i % 10;
                int r = i / 10;
                row = r;
                cel = c;
                getView().getUserMem().getCell(r, c).getElement().getStyle().setBackgroundColor("#FFFF00");

                getRegisters().TIME = 1;
                callRun();
            }
        };
    }
    
    private ProcessStatus getVMProcessStatus(VM vm) {
        LinkedList<ProcessDescriptor> pds = rm.getSuperMemory().getProcessDescriptors();
        for (ProcessDescriptor pd : pds) {
            if (pd.getType().equals(ProcessType.VM) && pd.getTitle().contentEquals(vm.name())) {
                return pd.getStatus();
            }
        }
        return null;// should never happen
    }

    public CPUDisplay getView() {
        return view;
    }

    private void updateRegisters() {
        LinkedList<Integer> list = rm.getRegisters().registerList();
        int i = 0;
        view.getCS().setText(list.get(i++) + "");
        view.getDS().setText(list.get(i++) + "");
        view.getSS().setText(list.get(i++) + "");
        view.getCP().setText(list.get(i++) + "");
        view.getSP().setText(list.get(i++) + "");
        view.getAR().setText(list.get(i++) + "");
        view.getBR().setText(list.get(i++) + "");
        view.getSF().setText(list.get(i++) + "");
        view.getPTR().setText(list.get(i++) + "");
        view.getSI().setText(list.get(i++) + "");
        view.getPI().setText(list.get(i++) + "");
        view.getTI().setText(list.get(i++) + "");
        view.getTIME().setText(list.get(i++) + "");
        view.getMODE().setText(list.get(i++) + "");
    }

    private void updateMemory() {
        String[] mem = rm.getMemory().getMemory();
        int r = 0;
        int c = 0;
        for (String w : mem) {
            getView().getUserMem().setCellValue(r, c, w);
            c++;
            if (c == Memory.WORDS_PER_BLOCK) {
                c = 0;
                r++;
            }
            if (r == Memory.BLOCKS_PER_VM * Memory.NUMBER_OF_VM) {
                break;
            }
        }

    }

    private void updateSuperMemory() {
        if (rm != null && rm.getSuperMemory() != null) {
            LinkedList<Descriptor> dList = rm.getSuperMemory().getMemory();
            MemTable sm = getView().getSuperMem();
            sm.clear();
            int row = 0;
            for (int i = 0; i < dList.size(); i++) {
                Descriptor d = dList.get(i);
                String s = null;
                if (d.getDescriptorType() == DescriptorType.PROCESS) {
                    ProcessDescriptor pd = (ProcessDescriptor) d;
                    s = pd.toString();
                } else if (d.getDescriptorType() == DescriptorType.RESOURCE) {
                    ResourceDescriptor rd = (ResourceDescriptor) d;
                    s = rd.toString();
                }
                int j = 0;
                int col = 0;
                while (j + 4 < s.length()) {
                    sm.setCellValue(row, col++, s.substring(j, j + 4));
                    j = j + 4;
                    if (col == Memory.WORDS_PER_BLOCK) {
                        row++;
                        col = 0;
                    }
                }
                if (j < s.length()) {
                    sm.setCellValue(row, col, s.substring(j, s.length()));
                }
                row++;
            }
        }
    }
}
